Skip to content

feat(tasks-panel): move task and automation filters server-side#3173

Open
rafavalls wants to merge 4 commits intomainfrom
rafavalls/server-side-task-filters
Open

feat(tasks-panel): move task and automation filters server-side#3173
rafavalls wants to merge 4 commits intomainfrom
rafavalls/server-side-task-filters

Conversation

@rafavalls
Copy link
Copy Markdown
Collaborator

@rafavalls rafavalls commented Apr 24, 2026

What is this contribution about?

Task member/type filters in the tasks panel were filtering already-fetched data client-side in TasksSection. Automation name search in the automations list was also a client-side array filter. Both are now server-side.

  • Tasks panel: memberFilter ("mine"/"all") now drives the owner param of the automation tasks query; typeFilter ("all"/"manual"/"automation") controls which server result-sets are included. Filter state lifted from TasksSection to TasksPanelContent.
  • Automations panel: AUTOMATION_LIST tool now accepts a search param, passed as an ILIKE condition in listWithTriggerCounts. useAutomations hook threads the param through; automations-list.tsx uses useTransition for a responsive input while the query refetches.

Screenshots/Demonstration

No visual change — behavior is identical, filtering now happens at the DB/query level.

How to Test

  1. Open the tasks panel and switch between "Mine only" / "All members" — verify only the correct tasks are shown (network request changes, not JS filter).
  2. Switch the type filter (All / Chats / Automation) — verify the list updates via a new fetch.
  3. Open an agent's Automations tab, type in the search box — verify the query is fired with the search argument rather than filtering the full list client-side.

Review Checklist

  • PR title is clear and descriptive
  • Changes are tested and working
  • Documentation is updated (if needed)
  • No breaking changes

Summary by cubic

Moved task/member/type filters to the server so we only fetch needed data, and added server-side search for automations. Fixed “All members” so it applies to both manual and automation queries; no UI changes.

  • Refactors

    • Tasks panel: memberFilter drives owner in useTasks ("me"/"all"); typeFilter selects result sets; filter state lifted to TasksPanelContent with useTransition; TasksSection renders server results (no local filtering).
    • Automations: AUTOMATION_LIST and storage accept optional search and apply ILIKE on names; useAutomations threads search with KEYS.automations; automations-list.tsx uses a transition-backed search input.
  • Bug Fixes

    • In “All members” mode, manual tasks were limited to the current user; both queries now share owner from memberFilter.

Written for commit 46c1390. Summary will update on new commits.

Task member/type filters now drive useTasks query params instead of
filtering already-fetched data in the component. Automation name search
uses an ILIKE query via the AUTOMATION_LIST tool instead of client-side
array filter.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

🧪 Benchmark

Should we run the Virtual MCP strategy benchmark for this PR?

React with 👍 to run the benchmark.

Reaction Action
👍 Run quick benchmark (10 & 128 tools)

Benchmark will run on the next push after you react.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 24, 2026

Release Options

Suggested: Minor (2.311.0) — based on feat: prefix

React with an emoji to override the release type:

Reaction Type Next Version
👍 Prerelease 2.310.12-alpha.1
🎉 Patch 2.310.12
❤️ Minor 2.311.0
🚀 Major 3.0.0

Current version: 2.310.11

Note: If multiple reactions exist, the smallest bump wins. If no reactions, the suggested bump is used (default: patch).

rafavalls and others added 2 commits April 24, 2026 09:52
myTasks was hardcoded to owner:"me" so in "All members" mode manual
tasks still only showed the current user's chats. Both queries now
share taskOwner derived from memberFilter.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…-task-filters

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

24 issues found across 343 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/mesh/src/cli/lib/clipboard.ts">

<violation number="1" location="apps/mesh/src/cli/lib/clipboard.ts:30">
P2: Handle `child.stdin` errors before writing so clipboard failures cannot surface as unhandled stream errors.</violation>
</file>

<file name="apps/mesh/src/web/components/chat/simple-mode-tier-dropdown.tsx">

<violation number="1" location="apps/mesh/src/web/components/chat/simple-mode-tier-dropdown.tsx:47">
P2: The dropdown trigger has no accessible name on small screens because its label text is hidden; add an `aria-label` to keep it screen-reader accessible at all breakpoints.</violation>
</file>

<file name="apps/docs/client/src/components/ui/ProductSwitcher.tsx">

<violation number="1" location="apps/docs/client/src/components/ui/ProductSwitcher.tsx:24">
P2: `useEffect` is banned in this repository, so this hook usage should be refactored to an allowed pattern (or explicitly justified with a lint disable if truly unavoidable).

(Based on your team's feedback about not using useEffect in this codebase.) [FEEDBACK_USED]</violation>
</file>

<file name="apps/mesh/src/api/utils/stream-guard.ts">

<violation number="1" location="apps/mesh/src/api/utils/stream-guard.ts:58">
P2: Do not forward `Content-Length` unchanged when wrapping a stream that may close early; it can advertise an incorrect body size after truncation.</violation>
</file>

<file name=".github/workflows/release-studio-sandbox.yaml">

<violation number="1" location=".github/workflows/release-studio-sandbox.yaml:58">
P1: Do not gate this path-scoped image workflow on pre-existing image tags; it can skip publishing after real sandbox changes.

(Based on your team's feedback about not skipping path-scoped image builds when a tag already exists.) [FEEDBACK_USED]</violation>
</file>

<file name="apps/mesh/src/web/components/thread/github/decode-html-entities.ts">

<violation number="1" location="apps/mesh/src/web/components/thread/github/decode-html-entities.ts:24">
P2: Numeric entity decoding can throw on out-of-range code points; guard the parsed value before calling `String.fromCodePoint`.</violation>
</file>

<file name="apps/mesh/src/web/hooks/use-automations.ts">

<violation number="1" location="apps/mesh/src/web/hooks/use-automations.ts:167">
P2: Normalize `search` in the query key to match request semantics; currently empty/null search values create different cache keys even though the request sent is identical.</violation>
</file>

<file name="apps/mesh/src/web/components/thread/github/comments-accordion.tsx">

<violation number="1" location="apps/mesh/src/web/components/thread/github/comments-accordion.tsx:42">
P1: Do not decode GitHub comment bodies before rendering markdown; it can turn escaped HTML into executable markup.

(Based on your team's feedback about avoiding HTML-entity decoding before MemoizedMarkdown for PR comments.) [FEEDBACK_USED]</violation>
</file>

<file name="apps/mesh/migrations/071-default-home-agents.ts">

<violation number="1" location="apps/mesh/migrations/071-default-home-agents.ts:3">
P1: This migration is not registered in the migrations index, so it will never be executed.</violation>
</file>

<file name="apps/mesh/src/api/middleware/log-deprecated-route.ts">

<violation number="1" location="apps/mesh/src/api/middleware/log-deprecated-route.ts:21">
P2: New middleware is not wired into runtime routes, so deprecation logging never runs outside tests.</violation>
</file>

<file name="apps/mesh/src/shared/github-clone-info.ts">

<violation number="1" location="apps/mesh/src/shared/github-clone-info.ts:30">
P2: This new shared `buildCloneInfo` function is currently unused, leaving dead code and duplicated clone-info logic in `tools/vm/start.ts`.</violation>
</file>

<file name="apps/mesh/src/web/components/chat/tiptap/build-improve-prompt-doc.ts">

<violation number="1" location="apps/mesh/src/web/components/chat/tiptap/build-improve-prompt-doc.ts:27">
P2: Escape `instructions` before inserting it into `<current_instructions>...</current_instructions>` so embedded closing tags cannot break the prompt structure.</violation>
</file>

<file name="apps/mesh/src/web/components/thread/github/use-pr-reviews.ts">

<violation number="1" location="apps/mesh/src/web/components/thread/github/use-pr-reviews.ts:70">
P2: `unresolvedConversations` is computed from total review comment count, which is not an unresolved-thread signal and can produce incorrect review status.</violation>
</file>

<file name="apps/mesh/src/web/components/thread/github/description-tab.tsx">

<violation number="1" location="apps/mesh/src/web/components/thread/github/description-tab.tsx:35">
P0: Do not decode HTML entities before rendering PR body markdown; this can turn escaped user content into executable/raw HTML.

(Based on your team's feedback about not decoding GitHub bodies before MemoizedMarkdown with rehype-raw.) [FEEDBACK_USED]</violation>
</file>

<file name="apps/mesh/migrations/072-ai-provider-key-preset-id.ts">

<violation number="1" location="apps/mesh/migrations/072-ai-provider-key-preset-id.ts:3">
P1: Register this new migration in `migrations/index.ts`; otherwise `preset_id` is never created and code expecting that column can fail at runtime.

(Based on your team's feedback about checking migration registration in migrations/index.ts before flagging.) [FEEDBACK_USED]</violation>
</file>

<file name="apps/mesh/migrations/075-thread-inflight-async-jobs.ts">

<violation number="1" location="apps/mesh/migrations/075-thread-inflight-async-jobs.ts:3">
P1: This migration is not registered in `migrations/index.ts`, so it will never run.

(Based on your team's feedback about verifying migration registration in migrations/index.ts.) [FEEDBACK_USED]</violation>
</file>

<file name="apps/mesh/migrations/070-model-categories.ts">

<violation number="1" location="apps/mesh/migrations/070-model-categories.ts:6">
P1: This migration duplicates `068-model-categories` by adding/dropping the same `simple_mode` column, which can cause migration failure when applying later migrations.</violation>
</file>

<file name="apps/mesh/migrations/073-backfill-basic-usage-roles.ts">

<violation number="1" location="apps/mesh/migrations/073-backfill-basic-usage-roles.ts:19">
P1: Migration not registered in `migrations/index.ts`. Without an import and map entry (e.g., `import * as migration073backfillbasicusageroles from './073-backfill-basic-usage-roles.ts'` and `"073-backfill-basic-usage-roles": migration073backfillbasicusageroles`), Kysely's `Migrator` will never execute this file.

(Based on your team's feedback about checking migration registration in index.ts.) [FEEDBACK_USED]</violation>
</file>

<file name="apps/mesh/src/web/components/settings/settings-section.tsx">

<violation number="1" location="apps/mesh/src/web/components/settings/settings-section.tsx:108">
P2: Guard the row key handler so Enter/Space only activates when the row itself has focus; otherwise nested action controls can trigger the parent click via keyboard bubbling.</violation>
</file>

<file name="apps/mesh/src/api/routes/thread-outputs.ts">

<violation number="1" location="apps/mesh/src/api/routes/thread-outputs.ts:57">
P2: This endpoint truncates thread outputs at 200 files because it reads only the first storage page and ignores continuation tokens.</violation>
</file>

<file name="apps/mesh/src/api/routes/vm-exec.ts">

<violation number="1" location="apps/mesh/src/api/routes/vm-exec.ts:68">
P2: Client request body is discarded. The proxy sends `body: null` to the daemon regardless of what the client POSTs. If the daemon's exec endpoint accepts parameters (arguments, env vars, etc.), they will be silently lost. Consider forwarding the body: `body: await c.req.raw.clone().body`.</violation>
</file>

<file name="apps/mesh/src/api/middleware/resolve-org-from-path.ts">

<violation number="1" location="apps/mesh/src/api/middleware/resolve-org-from-path.ts:84">
P1: Object storage is only rebuilt when null, but the middleware unconditionally overrides every other org-scoped field. If `ctx.objectStorage` was already constructed for a different org (e.g., from an active session), it will remain bound to the wrong org while all other context fields point to the path-resolved org. Consider always rebuilding it (matching the unconditional `setOrganizationId` pattern used for threads storage) to prevent cross-org storage access if preconditions change.</violation>
</file>

<file name="apps/mesh/src/web/hooks/use-org-auth-client.ts">

<violation number="1" location="apps/mesh/src/web/hooks/use-org-auth-client.ts:86">
P2: `getFullOrganization` defaults to the session's active organization when no `organizationId` is passed (per Better Auth docs), making it vulnerable to the same cross-tab issue this hook is designed to prevent. It should be wrapped with `withQueryOrgId` rather than passed through directly.</violation>
</file>

<file name="apps/mesh/migrations/069-sandbox-runner-state.ts">

<violation number="1" location="apps/mesh/migrations/069-sandbox-runner-state.ts:19">
P2: Use a SQL expression for the timestamp default; `defaultTo("now()")` is treated as a string literal, not a `now()` function call.</violation>
</file>

Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed. cubic prioritizes the most important files to review.
On a pro plan you can use ultrareview for larger PRs.
Tip: cubic used a learning from your PR history. Let your coding agent read cubic learnings directly with the cubic MCP.

<div className="text-sm">
<MemoizedMarkdown
id={`pr-body-${pr.number}`}
text={decodeHtmlEntities(pr.body)}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0: Do not decode HTML entities before rendering PR body markdown; this can turn escaped user content into executable/raw HTML.

(Based on your team's feedback about not decoding GitHub bodies before MemoizedMarkdown with rehype-raw.)

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/components/thread/github/description-tab.tsx, line 35:

<comment>Do not decode HTML entities before rendering PR body markdown; this can turn escaped user content into executable/raw HTML.

(Based on your team's feedback about not decoding GitHub bodies before MemoizedMarkdown with rehype-raw.) </comment>

<file context>
@@ -0,0 +1,46 @@
+        <div className="text-sm">
+          <MemoizedMarkdown
+            id={`pr-body-${pr.number}`}
+            text={decodeHtmlEntities(pr.body)}
+          />
+        </div>
</file context>

fi

- name: Setup Bun
if: steps.tag-check.outputs.exists != 'true'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Do not gate this path-scoped image workflow on pre-existing image tags; it can skip publishing after real sandbox changes.

(Based on your team's feedback about not skipping path-scoped image builds when a tag already exists.)

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/release-studio-sandbox.yaml, line 58:

<comment>Do not gate this path-scoped image workflow on pre-existing image tags; it can skip publishing after real sandbox changes.

(Based on your team's feedback about not skipping path-scoped image builds when a tag already exists.) </comment>

<file context>
@@ -0,0 +1,124 @@
+          fi
+
+      - name: Setup Bun
+        if: steps.tag-check.outputs.exists != 'true'
+        uses: oven-sh/setup-bun@v2
+        with:
</file context>

</div>
<MemoizedMarkdown
id={`pr-comment-${c.id}`}
text={decodeHtmlEntities(c.body)}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Do not decode GitHub comment bodies before rendering markdown; it can turn escaped HTML into executable markup.

(Based on your team's feedback about avoiding HTML-entity decoding before MemoizedMarkdown for PR comments.)

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/components/thread/github/comments-accordion.tsx, line 42:

<comment>Do not decode GitHub comment bodies before rendering markdown; it can turn escaped HTML into executable markup.

(Based on your team's feedback about avoiding HTML-entity decoding before MemoizedMarkdown for PR comments.) </comment>

<file context>
@@ -0,0 +1,64 @@
+              </div>
+              <MemoizedMarkdown
+                id={`pr-comment-${c.id}`}
+                text={decodeHtmlEntities(c.body)}
+              />
+            </div>
</file context>
Suggested change
text={decodeHtmlEntities(c.body)}
text={c.body}

@@ -0,0 +1,15 @@
import { Kysely } from "kysely";

export async function up(db: Kysely<unknown>): Promise<void> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: This migration is not registered in the migrations index, so it will never be executed.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/migrations/071-default-home-agents.ts, line 3:

<comment>This migration is not registered in the migrations index, so it will never be executed.</comment>

<file context>
@@ -0,0 +1,15 @@
+import { Kysely } from "kysely";
+
+export async function up(db: Kysely<unknown>): Promise<void> {
+  await db.schema
+    .alterTable("organization_settings")
</file context>

@@ -0,0 +1,15 @@
import { type Kysely } from "kysely";

export async function up(db: Kysely<unknown>): Promise<void> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Register this new migration in migrations/index.ts; otherwise preset_id is never created and code expecting that column can fail at runtime.

(Based on your team's feedback about checking migration registration in migrations/index.ts before flagging.)

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/migrations/072-ai-provider-key-preset-id.ts, line 3:

<comment>Register this new migration in `migrations/index.ts`; otherwise `preset_id` is never created and code expecting that column can fail at runtime.

(Based on your team's feedback about checking migration registration in migrations/index.ts before flagging.) </comment>

<file context>
@@ -0,0 +1,15 @@
+import { type Kysely } from "kysely";
+
+export async function up(db: Kysely<unknown>): Promise<void> {
+  await db.schema
+    .alterTable("ai_provider_keys")
</file context>

onKeyDown={
onClick
? (e) => {
if (e.key === "Enter" || e.key === " ") {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Guard the row key handler so Enter/Space only activates when the row itself has focus; otherwise nested action controls can trigger the parent click via keyboard bubbling.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/components/settings/settings-section.tsx, line 108:

<comment>Guard the row key handler so Enter/Space only activates when the row itself has focus; otherwise nested action controls can trigger the parent click via keyboard bubbling.</comment>

<file context>
@@ -0,0 +1,166 @@
+      onKeyDown={
+        onClick
+          ? (e) => {
+              if (e.key === "Enter" || e.key === " ") {
+                e.preventDefault();
+                onClick();
</file context>

}
const result = await storage.list({
prefix: `model-outputs/${threadId}/`,
maxKeys: 200,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This endpoint truncates thread outputs at 200 files because it reads only the first storage page and ignores continuation tokens.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/api/routes/thread-outputs.ts, line 57:

<comment>This endpoint truncates thread outputs at 200 files because it reads only the first storage page and ignores continuation tokens.</comment>

<file context>
@@ -0,0 +1,85 @@
+    }
+    const result = await storage.list({
+      prefix: `model-outputs/${threadId}/`,
+      maxKeys: 200,
+    });
+
</file context>

upstream = await runner.proxyDaemonRequest(claimName, daemonPath, {
method: "POST",
headers: new Headers(),
body: null,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Client request body is discarded. The proxy sends body: null to the daemon regardless of what the client POSTs. If the daemon's exec endpoint accepts parameters (arguments, env vars, etc.), they will be silently lost. Consider forwarding the body: body: await c.req.raw.clone().body.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/api/routes/vm-exec.ts, line 68:

<comment>Client request body is discarded. The proxy sends `body: null` to the daemon regardless of what the client POSTs. If the daemon's exec endpoint accepts parameters (arguments, env vars, etc.), they will be silently lost. Consider forwarding the body: `body: await c.req.raw.clone().body`.</comment>

<file context>
@@ -0,0 +1,98 @@
+    upstream = await runner.proxyDaemonRequest(claimName, daemonPath, {
+      method: "POST",
+      headers: new Headers(),
+      body: null,
+    });
+  } catch (err) {
</file context>

// Cross-org or pre-org operations.
list: authClient.organization.list,
create: authClient.organization.create,
getFullOrganization: authClient.organization.getFullOrganization,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: getFullOrganization defaults to the session's active organization when no organizationId is passed (per Better Auth docs), making it vulnerable to the same cross-tab issue this hook is designed to prevent. It should be wrapped with withQueryOrgId rather than passed through directly.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/hooks/use-org-auth-client.ts, line 86:

<comment>`getFullOrganization` defaults to the session's active organization when no `organizationId` is passed (per Better Auth docs), making it vulnerable to the same cross-tab issue this hook is designed to prevent. It should be wrapped with `withQueryOrgId` rather than passed through directly.</comment>

<file context>
@@ -0,0 +1,89 @@
+      // Cross-org or pre-org operations.
+      list: authClient.organization.list,
+      create: authClient.organization.create,
+      getFullOrganization: authClient.organization.getFullOrganization,
+    },
+  };
</file context>

.addColumn("handle", "text", (col) => col.notNull())
.addColumn("state", "jsonb", (col) => col.notNull())
.addColumn("updated_at", "timestamptz", (col) =>
col.notNull().defaultTo("now()"),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Use a SQL expression for the timestamp default; defaultTo("now()") is treated as a string literal, not a now() function call.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/migrations/069-sandbox-runner-state.ts, line 19:

<comment>Use a SQL expression for the timestamp default; `defaultTo("now()")` is treated as a string literal, not a `now()` function call.</comment>

<file context>
@@ -0,0 +1,38 @@
+    .addColumn("handle", "text", (col) => col.notNull())
+    .addColumn("state", "jsonb", (col) => col.notNull())
+    .addColumn("updated_at", "timestamptz", (col) =>
+      col.notNull().defaultTo("now()"),
+    )
+    .addPrimaryKeyConstraint("sandbox_runner_state_pkey", [
</file context>

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant